/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ohosannotations.internal.core.handler;

import com.helger.jcodemodel.AbstractJClass;
import com.helger.jcodemodel.IJExpression;
import com.helger.jcodemodel.JBlock;
import com.helger.jcodemodel.JFieldVar;
import com.helger.jcodemodel.JInvocation;
import com.helger.jcodemodel.JOp;
import com.helger.jcodemodel.JVar;

import org.ohosannotations.ElementValidation;
import org.ohosannotations.OhosAnnotationsEnvironment;
import org.ohosannotations.annotations.ReceiverAction;
import org.ohosannotations.handler.AnnotationHandler;
import org.ohosannotations.handler.BaseAnnotationHandler;
import org.ohosannotations.handler.HasParameterHandlers;
import org.ohosannotations.helper.CanonicalNameConstants;
import org.ohosannotations.helper.CaseHelper;
import org.ohosannotations.holder.EReceiverHolder;

import java.util.Collections;
import java.util.List;

import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;

import static com.helger.jcodemodel.JExpr._new;
import static com.helger.jcodemodel.JExpr._null;
import static com.helger.jcodemodel.JExpr.lit;
import static com.helger.jcodemodel.JMod.FINAL;
import static com.helger.jcodemodel.JMod.PUBLIC;
import static com.helger.jcodemodel.JMod.STATIC;

/**
 * 接收机操作处理程序
 *
 * @author dev
 * @since 2021-07-21
 */
public class ReceiverActionHandler extends BaseAnnotationHandler<EReceiverHolder>
    implements HasParameterHandlers<EReceiverHolder> {
    private ExtraHandler extraHandler;

    /**
     * 接收机操作处理程序
     *
     * @param environment 环境
     */
    public ReceiverActionHandler(OhosAnnotationsEnvironment environment) {
        super(ReceiverAction.class, environment);
        extraHandler = new ExtraHandler(environment);
    }

    /**
     * 得到参数处理程序
     *
     * @return {@link Iterable}
     */
    @Override
    public Iterable<AnnotationHandler> getParameterHandlers() {
        return Collections.<AnnotationHandler>singleton(extraHandler);
    }

    /**
     * 验证
     *
     * @param element 元素
     * @param valid 有效的
     */
    @Override
    protected void validate(Element element, ElementValidation valid) {
        validatorHelper.enclosingElementHasEReceiver(element, valid);

        validatorHelper.returnTypeIsVoid((ExecutableElement) element, valid);

        validatorHelper.isNotPrivate(element, valid);

        validatorHelper.param.anyOrder() //
            .type(CanonicalNameConstants.CONTEXT).optional() //
            .type(CanonicalNameConstants.INTENT).optional() //
            .annotatedWith(ReceiverAction.Extra.class).multiple().optional() //
            .validate((ExecutableElement) element, valid);
    }

    /**
     * 过程
     *
     * @param element 元素
     * @param holder 持有人
     * @throws Exception 异常
     */
    @Override
    public void process(Element element, EReceiverHolder holder) throws Exception {
        ExecutableElement executableElement = (ExecutableElement) element;
        String methodName = element.getSimpleName().toString();

        ReceiverAction annotation = element.getAnnotation(ReceiverAction.class);
        String[] dataSchemes = annotation.dataSchemes();
        String[] actions = annotation.actions();

        JFieldVar actionKeyField = createStaticField(holder, "actions", methodName, actions);
        JFieldVar dataSchemesField = createStaticField(holder, "dataSchemes", methodName, dataSchemes);
        addActionInOnReceive(holder, executableElement, methodName, actionKeyField, dataSchemesField);
    }

    /**
     * 创建静态字段
     *
     * @param holder 持有人
     * @param prefix 前缀
     * @param methodName 方法名称
     * @param values 值
     * @return {@link JFieldVar}
     */
    private JFieldVar createStaticField(EReceiverHolder holder, String prefix, String methodName, String[] values) {
        String staticFieldName = CaseHelper.camelCaseToUpperSnakeCase(prefix, methodName, null);

        if (values == null || values.length == 0) {
            return null;
        } else if (values.length == 1) {
            return holder.getGeneratedClass()
                .field(PUBLIC | STATIC | FINAL, getClasses().STRING, staticFieldName, lit(values[0]));
        } else {
        }

        JInvocation asListInvoke = getClasses().ARRAYS.staticInvoke("asList");
        for (String scheme : values) {
            asListInvoke.arg(scheme);
        }
        AbstractJClass listOfStrings = getClasses().LIST.narrow(getClasses().STRING);
        return holder.getGeneratedClass()
            .field(PUBLIC | STATIC | FINAL, listOfStrings, staticFieldName, asListInvoke);
    }

    /**
     * 收到上添加行动
     *
     * @param holder 持有人
     * @param executableElement 可执行元素
     * @param methodName 方法名称
     * @param actionsField 行为领域
     * @param dataSchemesField 数据计划字段
     */
    private void addActionInOnReceive(EReceiverHolder holder,
        ExecutableElement executableElement,
        String methodName, JFieldVar actionsField, JFieldVar dataSchemesField) {
        String actionsInvoke = getInvocationName(actionsField);
        IJExpression filterCondition = actionsField.invoke(actionsInvoke)
            .arg(holder.getOnReceiveIntentAction());
        if (dataSchemesField != null) {
            String dataSchemesInvoke = getInvocationName(dataSchemesField);
            filterCondition = filterCondition.cand(dataSchemesField
                .invoke(dataSchemesInvoke).arg(holder.getOnReceiveIntentDataScheme()));
        }

        JBlock callActionBlock = holder.getOnReceiveBody()._if(filterCondition)._then();
        IJExpression receiverRef = holder.getGeneratedClass().staticRef("this");
        JInvocation callActionInvocation = receiverRef.invoke(methodName);

        JVar intent = holder.getOnReceiveData();
        JVar extras = null;

        List<? extends VariableElement> methodParameters = executableElement.getParameters();
        for (VariableElement param : methodParameters) {
            AbstractJClass extraParamClass = codeModelHelper.typeMirrorToJClass(param.asType());

            if (extraParamClass.equals(getClasses().INTENT)
                && param.getAnnotation(ReceiverAction.Extra.class) == null) {
                callActionInvocation.arg(intent);
            } else if (param.getAnnotation(ReceiverAction.Extra.class) != null) {
                if (extras == null) {
                    extras = callActionBlock.decl(getClasses().PACMAP, "extras_",
                        JOp.cond(intent.invoke("getExtras")
                            .ne(_null()), intent.invoke("getExtras"), _new(getClasses().PACMAP)));
                }
                callActionInvocation.arg(extraHandler.getExtraValue(param, extras, callActionBlock, holder));
            } else {
            }
        }
        callActionBlock.add(callActionInvocation);
        callActionBlock._return();
    }

    /**
     * 调用名字
     *
     * @param field 场
     * @return {@link String}
     */
    private String getInvocationName(JFieldVar field) {
        AbstractJClass listOfStrings = getClasses().LIST.narrow(getClasses().STRING);
        if (field.type().fullName().equals(listOfStrings.fullName())) {
            return "contains";
        }
        return "equals";
    }

    /**
     * 额外的处理程序
     *
     * @author dev
     * @since 2021-07-23
     */
    private static class ExtraHandler extends ExtraParameterHandler {
        ExtraHandler(OhosAnnotationsEnvironment environment) {
            super(ReceiverAction.Extra.class, ReceiverAction.class, environment);
        }

        /**
         * getAnnotationValue
         *
         * @param parameter parameter
         * @return parameter
         */
        @Override
        public String getAnnotationValue(VariableElement parameter) {
            return parameter.getAnnotation(ReceiverAction.Extra.class).value();
        }

        /**
         * getExtraValue
         *
         * @param parameter parameter
         * @param extras extras
         * @param block block
         * @param holder holder
         * @return getExtraValue
         */
        public IJExpression getExtraValue(VariableElement parameter,
            JVar extras, JBlock block, EReceiverHolder holder) {
            return getExtraValue(parameter, extras, block, holder.getOnReceiveMethod(), holder);
        }
    }
}

